package com.llb666.shop.gateway.filter;

import com.llb666.common.core.utils.ServletUtils;
import com.llb666.common.core.web.error.CommonError;
import com.llb666.shop.gateway.client.AuthService;
import com.llb666.shop.gateway.client.bo.Account;
import com.llb666.shop.gateway.client.bo.AuthResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 权限过滤器：未登录用户不能访问用户中心接口.
 *
 * @author llb666
 * @version 2.0
 */
@Slf4j
@Component
public class GatewayAuthFilter extends AbstractGatewayFilterFactory<GatewayAuthFilter.Config> {

    private static final String AUTH = "headerUserToken";
    private static final String USER_ID = "headerUserId";

    private final AuthService authService;

    // @Lazy 解决循环依赖
    public GatewayAuthFilter(@Lazy AuthService authService) {
        super(Config.class);
        this.authService = authService;
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String url = exchange.getRequest().getURI().getPath();
            // 排除黑名单，不需要校验权限。比如登录，注册等接口不需要权限校验。其他地址需要经过权限检验.
            if (!config.matchBlacklist(url)) {
                log.info("auth start");
                ServerHttpRequest request = exchange.getRequest();
                HttpHeaders headers = request.getHeaders();
                String token = headers.getFirst(AUTH);
                String userId = headers.getFirst(USER_ID);

                ServerHttpResponse response = exchange.getResponse();
                if (StringUtils.isBlank(token)) {
                    log.error("token不存在！");
                    response.setStatusCode(HttpStatus.UNAUTHORIZED);
                    return unauthorizedResponse(exchange, "令牌不能为空");
                }
                Account account = Account.builder().userId(userId).token(token).build();
                AuthResponse verify = authService.verify(account);
                if (verify.getCode() != 1L) {
                    log.error("token校验失败！");
                    response.setStatusCode(HttpStatus.FORBIDDEN);
                    return unauthorizedResponse(exchange, "令牌检验失败");
                }
                // 用户信息存入header
                ServerHttpRequest.Builder mutate = request.mutate();
                mutate.header(USER_ID, userId);
                mutate.header(AUTH, token);
                ServerHttpRequest buildRequest = mutate.build();

                response.setStatusCode(HttpStatus.OK);
                response.getHeaders().add(USER_ID, userId);
                response.getHeaders().add(AUTH, token);
                return chain.filter(exchange.mutate()
                        .request(buildRequest)
                        .response(response)
                        .build());
            }
            return chain.filter(exchange);
        };
    }


    public static class Config {
        private List<String> blacklistUrl;

        private final List<Pattern> blacklistUrlPattern = new ArrayList<>();

        public boolean matchBlacklist(String url) {
            return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find());
        }

        public List<String> getBlacklistUrl() {
            return blacklistUrl;
        }

        public void setBlacklistUrl(List<String> blacklistUrl) {
            this.blacklistUrl = blacklistUrl;
            this.blacklistUrlPattern.clear();
            this.blacklistUrl.forEach(url -> {
                this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE));
            });
        }
    }

    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
        return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, CommonError.NeedAuthentication);
    }
}
